import { Message, MessageProcessResult } from '@/core/message';
import { Logger } from '@/types';
import { createLogger } from '@/utils/logger';

/**
 * Middleware context for passing data between middleware
 */
export interface MiddlewareContext {
  message: Message;
  metadata: Record<string, unknown>;
  startTime: number;
  correlationId?: string;
  traceId?: string;
  spanId?: string;
}

/**
 * Base middleware interface
 */
export interface Middleware {
  /**
   * Called before sending a message
   */
  beforeSend?(context: MiddlewareContext): Promise<MiddlewareContext> | MiddlewareContext;

  /**
   * Called after sending a message
   */
  afterSend?(context: MiddlewareContext, result: boolean): Promise<void> | void;

  /**
   * Called before processing a message
   */
  beforeProcess?(context: MiddlewareContext): Promise<MiddlewareContext> | MiddlewareContext;

  /**
   * Called after processing a message
   */
  afterProcess?(context: MiddlewareContext, result: MessageProcessResult): Promise<void> | void;

  /**
   * Called when an error occurs
   */
  onError?(context: MiddlewareContext, error: Error): Promise<void> | void;
}

/**
 * Middleware pipeline for executing middleware in order
 */
export class MiddlewarePipeline {
  private readonly middlewares: Middleware[] = [];

  /**
   * Add middleware to the pipeline
   */
  add(middleware: Middleware): MiddlewarePipeline {
    this.middlewares.push(middleware);
    return this;
  }

  /**
   * Remove middleware from the pipeline
   */
  remove(middleware: Middleware): boolean {
    const index = this.middlewares.indexOf(middleware);
    if (index !== -1) {
      this.middlewares.splice(index, 1);
      return true;
    }
    return false;
  }

  /**
   * Clear all middleware
   */
  clear(): void {
    this.middlewares.length = 0;
  }

  /**
   * Get all middleware
   */
  getMiddlewares(): readonly Middleware[] {
    return this.middlewares;
  }

  /**
   * Execute beforeSend middleware
   */
  async executeBeforeSend(context: MiddlewareContext): Promise<MiddlewareContext> {
    let currentContext = context;

    for (const middleware of this.middlewares) {
      if (middleware.beforeSend) {
        try {
          currentContext = await middleware.beforeSend(currentContext);
        } catch (error) {
          await this.executeOnError(currentContext, error as Error);
          throw error;
        }
      }
    }

    return currentContext;
  }

  /**
   * Execute afterSend middleware
   */
  async executeAfterSend(context: MiddlewareContext, result: boolean): Promise<void> {
    // Execute in reverse order
    for (let i = this.middlewares.length - 1; i >= 0; i--) {
      const middleware = this.middlewares[i];
      if (middleware.afterSend) {
        try {
          await middleware.afterSend(context, result);
        } catch (error) {
          await this.executeOnError(context, error as Error);
          // Don't rethrow in afterSend to avoid breaking the flow
        }
      }
    }
  }

  /**
   * Execute beforeProcess middleware
   */
  async executeBeforeProcess(context: MiddlewareContext): Promise<MiddlewareContext> {
    let currentContext = context;

    for (const middleware of this.middlewares) {
      if (middleware.beforeProcess) {
        try {
          currentContext = await middleware.beforeProcess(currentContext);
        } catch (error) {
          await this.executeOnError(currentContext, error as Error);
          throw error;
        }
      }
    }

    return currentContext;
  }

  /**
   * Execute afterProcess middleware
   */
  async executeAfterProcess(
    context: MiddlewareContext,
    result: MessageProcessResult
  ): Promise<void> {
    // Execute in reverse order
    for (let i = this.middlewares.length - 1; i >= 0; i--) {
      const middleware = this.middlewares[i];
      if (middleware.afterProcess) {
        try {
          await middleware.afterProcess(context, result);
        } catch (error) {
          await this.executeOnError(context, error as Error);
          // Don't rethrow in afterProcess to avoid breaking the flow
        }
      }
    }
  }

  /**
   * Execute onError middleware
   */
  async executeOnError(context: MiddlewareContext, error: Error): Promise<void> {
    for (const middleware of this.middlewares) {
      if (middleware.onError) {
        try {
          await middleware.onError(context, error);
        } catch (middlewareError) {
          // Log middleware errors but don't throw to avoid infinite loops
          console.error('Error in middleware onError handler:', middlewareError);
        }
      }
    }
  }

  /**
   * Create middleware context
   */
  createContext(message: Message, metadata: Record<string, unknown> = {}): MiddlewareContext {
    return {
      message,
      metadata,
      startTime: Date.now(),
      correlationId: message.getProperty('correlationId') as string,
      traceId: message.getHeader('traceId') as string,
      spanId: message.getHeader('spanId') as string,
    };
  }
}

/**
 * Abstract base class for middleware implementations
 */
export abstract class BaseMiddleware implements Middleware {
  protected readonly name: string;
  protected readonly logger?: Logger;

  constructor(name: string, logger?: Logger) {
    this.name = name;
    this.logger = logger || createLogger(this.name);
  }

  protected log(
    level: 'debug' | 'info' | 'warn' | 'error',
    message: string,
    meta?: Record<string, unknown>
  ): void {
    if (this.logger) {
      if (level === 'error') {
        this.logger.error(`[${this.name}] ${message}`, undefined, meta);
      } else {
        this.logger[level](`[${this.name}] ${message}`, meta);
      }
    }
  }

  async beforeSend?(context: MiddlewareContext): Promise<MiddlewareContext> {
    return context;
  }

  async afterSend?(_context: MiddlewareContext, _result: boolean): Promise<void> {
    // Default implementation does nothing
  }

  async beforeProcess?(context: MiddlewareContext): Promise<MiddlewareContext> {
    return context;
  }

  async afterProcess?(_context: MiddlewareContext, _result: MessageProcessResult): Promise<void> {
    // Default implementation does nothing
  }

  async onError?(context: MiddlewareContext, error: Error): Promise<void> {
    this.log('error', `Error in middleware: ${error.message}`, {
      messageId: context.message.id,
      error: error.stack,
    });
  }
}

/**
 * Conditional middleware that only executes if a condition is met
 */
export class ConditionalMiddleware extends BaseMiddleware {
  private readonly condition: (context: MiddlewareContext) => boolean;
  private readonly wrappedMiddleware: Middleware;

  constructor(
    condition: (context: MiddlewareContext) => boolean,
    middleware: Middleware,
    name = 'ConditionalMiddleware',
    logger?: Logger
  ) {
    super(name, logger);
    this.condition = condition;
    this.wrappedMiddleware = middleware;
  }

  async beforeSend(context: MiddlewareContext): Promise<MiddlewareContext> {
    if (this.condition(context) && this.wrappedMiddleware.beforeSend) {
      return await this.wrappedMiddleware.beforeSend(context);
    }
    return context;
  }

  async afterSend(context: MiddlewareContext, result: boolean): Promise<void> {
    if (this.condition(context) && this.wrappedMiddleware.afterSend) {
      await this.wrappedMiddleware.afterSend(context, result);
    }
  }

  async beforeProcess(context: MiddlewareContext): Promise<MiddlewareContext> {
    if (this.condition(context) && this.wrappedMiddleware.beforeProcess) {
      return await this.wrappedMiddleware.beforeProcess(context);
    }
    return context;
  }

  async afterProcess(context: MiddlewareContext, result: MessageProcessResult): Promise<void> {
    if (this.condition(context) && this.wrappedMiddleware.afterProcess) {
      await this.wrappedMiddleware.afterProcess(context, result);
    }
  }

  async onError(context: MiddlewareContext, error: Error): Promise<void> {
    if (this.condition(context) && this.wrappedMiddleware.onError) {
      await this.wrappedMiddleware.onError(context, error);
    }
  }
}

/**
 * Utility functions for creating middleware
 */
export const createMiddlewarePipeline = (): MiddlewarePipeline => {
  return new MiddlewarePipeline();
};

export const createConditionalMiddleware = (
  condition: (context: MiddlewareContext) => boolean,
  middleware: Middleware,
  name?: string,
  logger?: Logger
): ConditionalMiddleware => {
  return new ConditionalMiddleware(condition, middleware, name, logger);
};
